[コスト大事] Amazon Athena で S3 の大量オブジェクトをクエリする場合はスキャン量だけでなくリクエスト量も意識しよう

[コスト大事] Amazon Athena で S3 の大量オブジェクトをクエリする場合はスキャン量だけでなくリクエスト量も意識しよう

Amazon Athena で大量の S3 オブジェクトをスキャンする場合、リクエストに付随する料金が無視できない量になります。1ドルくらいかなと思ったら20ドルかかった、という話です。

コンバンハ、千葉(幸)です。

みなさん、Amazon Athena(以降 Athena) 、使ってますか?

Athena を利用すれば Amazon S3(以降 S3) 上のデータに対してクエリを実行できます。S3 バケット上に出力された各種ログに対して Athena でクエリをかけて必要な情報を取得する、というのはよくある使い方です。

最近 S3 バケット上の AWS CloudTrail(以降 CloudTrail)ログを Atena でクエリしたい機会がありました。

Athena ってデータをスキャンした量に応じて課金されるよな。1 TB スキャンして 5 USD くらいの料金だったよな。」という理解でいたので、1 USD もかからないくらいの料金でおさまるだろうと踏んでいました。

結果、15回強のクエリを行って約 20 USD のコストが発生しました。

スキャン量だけを気にするのではいけないんだな〜というのを学んだので、このブログで残しておきます。

先にまとめ

  • Athena のクエリに伴い S3 オブジェクトへの Get リクエストが発生する
  • Athena のクエリ実行に付随して発生する S3 へのアクセスは S3 の標準料金で課金される(ストレージ、リクエスト、データ転送)
  • 大量の S3 オブジェクトをクエリする場合は S3 データイベントに関連する機能の有効化状況を意識しよう
  • そもそもクエリ対象をなるべく絞ろう

Athena 実行時にはスキャン量だけでなくリクエスト量も意識

今回想定よりコストが膨らんだ要因は S3 へのリクエストが大量に発生したこと(その仕様を意識できていなかったこと)です。

具体的なコストの内訳としては S3 のリクエスト料金Amazon GuardDuty(以降 GuardDuty)による S3 データイベント分析料金が一定量かかりました。

Athena のクエリに伴う料金発生の全体像は以下です。

Athena cost
オレンジで表している部分がコスト発生箇所です

今回の料金内訳は以下の通りです。[1]

# サービス タイプ 説明 数量 コスト(USD)
1 Athena APN1-DataScannedInTB 5.00 USD per Terabytes for DataScannedInTB in Asia Pacific (Tokyo) 0.05 0.25
2 S3 APN1-Requests-Tier2 $0.0037 per 10,000 GET and all other requests 13,925,064 5.15
3 GuardDuty APN1-PaidS3DataEventsAnalyzed $0.00000104 per S3 Data Event for the first 500000000 events / month analyzed in Asia Pacific (Tokyo) region 13,923,408 14.48

意識していた Athena のスキャン量に応じた料金(#1)は想定通り 1 USD 以下に収まっていますが、それ以外の観点(#2,#3)でコストが嵩んでいます。

なお、#3は わたしの環境で GuardDuty S3 Protection 機能を明示的に有効化していたために発生した料金です。

発生しなかったけど発生する可能性があった料金

CloudTrail 証跡でデータイベントの記録を有効化していた場合、「配信されたデータイベント 100,000 あたり 0.10 USD」の料金がかかります。今回で言えば、Athena のクエリ対象である「CloudTrail ログが格納された S3 バケット」への S3 データイベントの記録を有効化していたらさらに料金がかかっていたということです。

#2,#3 と同等の約 1390万件の S3 オブジェクトの Get が記録された場合、13.9 USD が発生していたことになります。

今回実行した Athena によるクエリの背景/補足

CloudTrail ログが格納された S3 バケット

今回は以下の前提で S3 バケットへのログ出力がなされていました。

  • 東京リージョンの CloudTrail 証跡からマルチリージョン証跡として S3 バケットへログ出力
  • S3 バケットではライフサイクルルールが設定されており、400日を超過したログは削除される
  • (S3 オブジェクトはすべて標準ストレージクラス)

つまり、過去 400 日間のすべての(有効な)リージョンの CloudTrail ログが S3 バケット上に格納されていた状態です。

S3 バケット上のデータを簡便に「メトリクス」から確認すると、約 100万のオブジェクトがあり、合計約 3.7 GB のサイズとなっていました。

Bucket_metrics

このバケットには CloudTrail ログ以外のデータも含まれているので、Athena のスキャン対象に絞って確認すると、以下の通り(オブジェクト数 約 83.7万、サイズ 3.3 GiB)となりました。[2]

AWS CLIによる特定のプレフィックス配下のオブジェクト数、サイズ確認
aws s3 ls S3バケット名/AWSLogs/000000000000/CloudTrail/ \
  --recursive --summarize --human-readable\
  > /tmp/hoge.txt && tail -n 2 /tmp/hoge.txt
Total Objects: 837137
   Total Size: 3.3 GiB

Athena テーブルの作成

今回の試行にあわせて新たに Athena テーブルの作成を行いました。

CloudTrail のコンソールより「Athena テーブルを作成」を選択し、CloudTrail ログが保管された S3 バケットを選択すると自動的にテーブル作成用のクエリが生成されます。

CloudTrail_Athena

生成されたクエリの内容は以下で、今回はそのまま実行しました。

Athenaテーブル作成クエリ
CREATE EXTERNAL TABLE cloudtrail_logs_バケット名 (
    eventVersion STRING,
    userIdentity STRUCT<
        type: STRING,
        principalId: STRING,
        arn: STRING,
        accountId: STRING,
        invokedBy: STRING,
        accessKeyId: STRING,
        userName: STRING,
        sessionContext: STRUCT<
            attributes: STRUCT<
                mfaAuthenticated: STRING,
                creationDate: STRING>,
            sessionIssuer: STRUCT<
                type: STRING,
                principalId: STRING,
                arn: STRING,
                accountId: STRING,
                username: STRING>,
            ec2RoleDelivery: STRING,
            webIdFederationData: MAP<STRING,STRING>>>,
    eventTime STRING,
    eventSource STRING,
    eventName STRING,
    awsRegion STRING,
    sourceIpAddress STRING,
    userAgent STRING,
    errorCode STRING,
    errorMessage STRING,
    requestParameters STRING,
    responseElements STRING,
    additionalEventData STRING,
    requestId STRING,
    eventId STRING,
    resources ARRAY<STRUCT<
        arn: STRING,
        accountId: STRING,
        type: STRING>>,
    eventType STRING,
    apiVersion STRING,
    readOnly STRING,
    recipientAccountId STRING,
    serviceEventDetails STRING,
    sharedEventID STRING,
    vpcEndpointId STRING,
    tlsDetails STRUCT<
        tlsVersion: STRING,
        cipherSuite: STRING,
        clientProvidedHostHeader: STRING>
)
COMMENT 'CloudTrail table for バケット bucket'
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://バケット名/AWSLogs/000000000000/CloudTrail/'
TBLPROPERTIES ('classification'='cloudtrail')

下から2行目のLOCATIONでプレフィックスを具体的に指定すればスキャン対象を限定できます。

Athena によるクエリの実行履歴

今回の一連の試行において実行したクエリの内訳は以下のとおりです。(テーブルを作る/作り直すという本筋に関係ない情報が含まれていたり、SQL の書き方の不備で何度も FAILED になっているのはご愛嬌です。)

# クエリ種別 ステータス 実行時間 スキャンしたデータ
1 CREATE EXTERNAL TABLE FAILED 1.932 sec 0 MB
2 SELECT FAILED 364 ms 0 MB
3 SELECT FAILED 186 ms 0 MB
4 SELECT FAILED 160 ms 0 MB
5 DROP TABLE SUCCEEDED 552 ms 0 MB
6 DROP TABLE SUCCEEDED 576 ms 0 MB
7 CREATE EXTERNAL TABLE SUCCEEDED 408 ms 0 MB
8 SELECT SUCCEEDED 6 sec 48.02 KB
9 SELECT FAILED 188 ms 0 MB
10 SELECT SUCCEEDED 1 min 47.289 sec 3.31 GB
11 SELECT SUCCEEDED 1 min 48.573 sec 3.31 GB
12 SELECT SUCCEEDED 1 min 50.459 sec 3.31 GB
13 SELECT SUCCEEDED 1 min 50.906 sec 3.31 GB
14 SELECT FAILED 181 ms 0 MB
15 SELECT FAILED 178 ms 0 MB
16 SELECT FAILED 176 ms 0 MB
17 SELECT FAILED 189 ms 0 MB
18 SELECT SUCCEEDED 1 min 48.229 sec 3.31 GB
19 SELECT SUCCEEDED 1 min 44.209 sec 3.31 GB
20 SELECT FAILED 339 ms 0 MB
21 SELECT CANCELLED 5.183 sec 0 MB
22 SELECT SUCCEEDED 1 min 59.338 sec 3.31 GB
23 SELECT FAILED 179 ms 0 MB
24 SELECT CANCELLED 5.511 sec 0 MB
25 SELECT CANCELLED 48.695 sec 1.59 GB
26 SELECT FAILED 160 ms 0 MB
27 SELECT CANCELLED 1.178 sec 0 MB
28 SELECT FAILED 251 ms 0 MB
29 SELECT FAILED 169 ms 0 MB
30 SELECT SUCCEEDED 1 min 41.431 sec 3.31 GB
31 SELECT SUCCEEDED 1 min 44.662 sec 3.31 GB
32 SELECT SUCCEEDED 1 min 40.542 sec 3.31 GB
33 SELECT SUCCEEDED 1 min 51.53 sec 3.31 GB
34 SELECT SUCCEEDED 1 min 44.747 sec 3.31 GB
35 SELECT SUCCEEDED 1 min 46.178 sec 3.31 GB
36 SELECT SUCCEEDED 1 min 38.994 sec 3.31 GB
37 SELECT SUCCEEDED 1 min 45.633 sec 3.31 GB
38 SELECT SUCCEEDED 1 min 47.959 sec 3.31 GB

基本的に SELECT では S3 バケット上の CloudTrail ログをすべて対象にスキャンしました。(なるべく対象を絞るべきではあるのですが、スキャン量しか意識していなかったので、クエリの内容を細かく考えるよりは全体指定でやっちゃおうという精神でした。)

全量をスキャンした場合は以下の結果になります。

  • スキャンしたデータサイズ 3.31 GB
  • 所要時間 1分40秒程度(長いなと思いながら律儀に待ちました)

途中でキャンセルしたものを0.5回とカウントすると、全量スキャンを行ったのは16.5回です。今回の一連の Athena クエリの試行の中で以下が行われました。

  • 約 54.6 GB のスキャン(3.31 GB * 16.5)
  • 約 1,380万回の GET リクエスト(約 83,700オブジェクト * 16.5)

Athena のクエリによって発生した料金の確認

Athena によるクエリを実行した数日後、AWS 管理コンソールの請求画面や CUR(AWS Cost and Usage Reports)をベースにした情報から料金を確認しました。

冒頭の再掲となりますが、以下の内訳となっていました。

サービス タイプ 説明 数量 コスト(USD)
Athena APN1-DataScannedInTB 5.00 USD per Terabytes for DataScannedInTB in Asia Pacific (Tokyo) 0.05 0.25
S3 APN1-Requests-Tier2 $0.0037 per 10,000 GET and all other requests 13,925,064 5.15
GuardDuty APN1-PaidS3DataEventsAnalyzed $0.00000104 per S3 Data Event for the first 500000000 events / month analyzed in Asia Pacific (Tokyo) region 13,923,408 14.48
  • Athena:50GB
  • S3:約1,390万回
  • GuardDuty:約1,390万回

……と、細部の数字のずれはありますが、ここまで試算した内容と一致しています。

Athena のスキャン量だけを意識していたわたしとしてはここでギョッとしました。

Athena のクエリにおける S3 リクエストの料金

Athena の料金ページには以下記述があります。

追加料金

Athena は、Amazon S3 から直接データをクエリ処理します。Athena によるデータのクエリ処理に対する追加のストレージ料金は発生しません。ストレージ、リクエスト、データ転送に対して S3 の標準料金が発生します。デフォルトでは、クエリ結果は選択した S3 バケットに保存され、S3 の標準料金が課金されます。

Athena のクエリ処理によって発生したストレージ、リクエスト、データ転送は S3 の標準料金で課金されます。

今回はストレージ、データ転送は無視できる程度の課金でしたが、対象オブジェクトが多かったためにリクエスト料金が嵩みました。

GuardDuty による S3 データイベント分析

GuardDuty S3 Protection 機能を有効化すると、GuardDuty による S3 のデータイベント分析が行われます。(利用者側で CloudTrail 証跡のデータイベント記録を有効化する必要はありません。)

S3 Protection が有効になっているかは GuardDuty コンソールの以下画面から確認できます。

GuardDuty_S3_protection

また、「使用状況」より GuardDuty コストの発生内訳が確認できます。

GuardDuty_Usage

S3 バケットごとの内訳も確認できます。今回は Athena のクエリ実行対象の S3 バケットで料金が嵩んでいることが確認できました。

GuardDuty_S3_usage

Athena による料金の発生を抑えるには?

今回のコスト発生の大きな要因は以下のとおりです。

  • スキャン対象にした S3 オブジェクトが大量である
    • スキャン対象が広い
    • 何度も試行している
  • S3 データイベント量に応じた課金が発生する機能を有効化していた

もし GuardDuty S3 Protection を有効化しておらず、かつ全量スキャンの試行を 5回程度に抑えていれば発生コストは2ドル程度でした。特に気に掛けるようなボリュームではありません。

とは言え、スキャン対象を適切な範囲にする、というのは常日頃から意識しておきべきです。以下のような取り組みで必要最小限の範囲へのクエリを行うようにしましょう。

また、S3 データイベントに関連した機能の有効化状況も押さえておきましょう。Athena クエリ実行のために機能のオン/オフをする必要はないでしょうが、「意図せずまとまった課金発生」は避けたいところです。

  • CloudTrail 証跡でのデータイベント記録
  • GuardDuty による S3 データイベント分析

まとめ(再掲)

  • Athena のクエリに伴い S3 オブジェクトへの Get リクエストが発生する
  • Athena のクエリ実行に付随して発生する S3 へのアクセスは S3 の標準料金で課金される(ストレージ、リクエスト、データ転送)
  • 大量の S3 オブジェクトをクエリする場合は S3 データイベントに関連する機能の有効化状況を意識しよう
  • そもそもクエリ対象をなるべく絞ろう

Athena 実行時の料金に注意

Amazon Athena によるクエリで想定外に料金がかかった、という話でした。

突き詰めて考えると、以下の部分をきちんと押さえられていなかったな、という点に尽きます。

Athena は、Amazon S3 から直接データをクエリ処理します。Athena によるデータのクエリ処理に対する追加のストレージ料金は発生しません。ストレージ、リクエスト、データ転送に対して S3 の標準料金が発生します。 デフォルトでは、クエリ結果は選択した S3 バケットに保存され、S3 の標準料金が課金されます。

「Athena 実行時にはスキャン量だけじゃなくてリクエスト量も意識しておかないとコストが膨らむことがある」という点だけ伝わっていれば幸いです。

以上、チバユキ (@batchicchi)がお送りしました。

参考

脚注
  1. 正確には今回の Athena クエリ実行以外のコストも含まれていますが、ほとんど無視できる量です。 ↩︎

  2. 今回は AWS CLI での確認を行いましたが、S3 Storage Lensから確認もできます。画面操作を少しポチポチする必要があり、少し手間になりそうだったので AWS CLI を選択しました。 ↩︎

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.